#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>
#include <sw/redis++/redis++.h>
#include <chrono>
#include <thread>

#include "util.hpp"

using std::cout;
using std::cin;
using std::string;
using std::endl;
using std::unordered_map;
using std::vector;

using sw::redis::Redis;
using sw::redis::StringView;

/*
Note: StringView 是 Redis 内部提供的一种字符串
      1. string 可以读也可以写
      2. StringView 只读
         StringView 针对只读操作，可以做很多的优化操作，效率会比 string 更高
         (c++17 也引入了 string_view)
      
      OptionalString 可以表示非法值/无效值 (c++14 引入了 optional)
      并且 其重写了 operator bool 运算符，可以被判定该对象是否有效

      redis.keys(pattern)
      这里的第二个参数是一个插入迭代器，需要先准备好保存结果的容器，再创建一个插入迭代器指向容器的位置
      就可以把 keys 获取到的结果依次通过刚刚的迭代器插入到容器中了
    
      STL 中迭代器由 5 种类型
      1. 输入迭代器
      2. 输出迭代器
      3. 前向迭代器
      4. 双向迭代器
      5. 随机访问迭代器
      插入迭代器算是  <输出迭代器>
      插入迭代器表示：位置 + 动作
      STL 提供了 3 个插入迭代器：
            font_insert_iterator        区间的开头，往前面插入                  font_inserter
            back_insert_iterator        区间的结尾，往后面插入                  back_inserter            
            insert_iterator             区间的任意位置，往该位置之前插入         inserter
      一般不会直接构造这些对象，一般会通过辅助函数来构造 --------------------------↑
      其主要的操作就是赋值运算，把另一个迭代器赋值给这个插入迭代器
            就是将 一个普通迭代器 指向的元素插入到 插入迭代器指向的容器末尾（相当于调用了一次 push_back)

      
*/

void GetSet(Redis& redis) {
    // 清空
    redis.flushdb();
    redis.set("name", "小刘");
    redis.set("sex", "男");
    redis.set("height", "180");

    // StringView 不支持运算符重载
    auto name_val = redis.get("name");
    cout << "name = " << name_val.value() << endl;

    auto sex_val = redis.get("sex");
    cout << "sex_val = " << sex_val.value() << endl;

    auto height_val = redis.get("height");
    cout << "height = " << height_val.value() << endl;

    // 该变量其实就是 optional 的非法状态
    auto no = redis.get("no");
    if (no)
        cout << no.value() << endl;
}

void Exists(Redis& redis) {
    auto ret = redis.exists("name");

    // 一次判断多个 KEY 是否存在，返回存在的 Key 的个数（int)
    ret = redis.exists({"name", "sex", "height"});
}

void Del(Redis& redis) {
    // 删除某个 key-value, 返回值为 long long
    redis.del("key");

    // 一次删除多个 key，返回值为成功删除的元素个数
    redis.del({"key1", "key2", "key3"});
}

void Keys(Redis& redis) {
    redis.flushdb();
    redis.set("a", "a");
    redis.set("b", "b");
    redis.set("c", "c");
    redis.set("d", "d");

    vector<string> result;
    // 就是返回一个 back_insert_iterator
    auto it = std::back_inserter(result);
    // 这里的迭代器可以直接将元素放到这个容器的最后面
    redis.keys("*", it);

    Print(result);
}

void Expire(Redis& redis) {
    // 引入这个命名空间，就可以方便地使用 3ms, 3s 这种操作
    using namespace std::chrono_literals;

    redis.flushdb();
    redis.set("a", "a");
    // 设置 10 秒过期, 返回值表示成功或者失败
    bool ret = redis.expire("a", 10s);

    // 让当前线程睡眠 3s
    std::this_thread::sleep_for(3s);

    int remain = redis.ttl("a");
    cout << "剩余时间 = " << remain << endl;
}

void Type(Redis& redis) {
    redis.flushdb();
    redis.set("key", "value");

    string type = redis.type("key");
    cout << "key 类型为 " << type << endl;

    redis.lpush("list", "111");
    type = redis.type("list");
    cout << "list 类型为 " << type << endl;

    redis.hset("hash", "field", "value");
    type = redis.type("hash");
    cout << "hash 类型为 " << type << endl;

    redis.sadd("set", "222");
    type = redis.type("set");
    cout << "set 类型为 " << type << endl;

    redis.zadd("zset", "语文", 99);
    type = redis.type("zset");
    cout << "zset 类型为 " << type << endl;
}

int main() {
    sw::redis::Redis redis("tcp://127.0.0.1:6379");
    GetSet(redis);

    Exists(redis);

    Del(redis);

    Keys(redis);

    Expire(redis);

    Type(redis);

    return 0;
}